---
title: 前端開發核心概念
description: 從後端開發思維轉向前端開發，理解瀏覽器環境，掌握客戶端程式設計和互動式使用者介面的基礎知識。
---

# 前端開發核心概念

作為習慣了後端開發的 Python 開發者，轉向前端開發需要理解根本不同的概念。本模組從後端開發者的角度介紹前端開發的核心概念。

## 理解客戶端-伺服器範式

### 後端 vs 前端開發

<PythonEditor title="後端 vs 前端開發理念" compare={true}>
```python !! py
# 後端開發 (Python)
# 伺服器端邏輯、資料處理、業務規則

from flask import Flask, jsonify
import json

app = Flask(__name__)

# 資料儲存在伺服器
users_database = [
    {"id": 1, "name": "小明", "email": "xiaoming@example.com"},
    {"id": 2, "name": "小紅", "email": "xiaohong@example.com"}
]

@app.route('/api/users', methods=['GET'])
def get_users():
    """
    後端特點：處理資料並返回 JSON
    - 處理業務邏輯
    - 管理資料庫連接
    - 集中處理請求
    """
    return jsonify({
        "status": "success",
        "data": users_database,
        "total": len(users_database)
    })

# 後端特徵：
# 1. 集中化處理
# 2. 資料庫管理
# 3. 業務邏輯處理
# 4. 安全和認證
print("後端：伺服器端處理，集中控制")
```

```javascript !! js
// 前端開發 (JavaScript)
// 客戶端邏輯、使用者介面、使用者體驗

// 從後端接收的資料
let users = [];

// 前端：處理使用者介面和互動
async function loadUsers() {
    /**
     * 前端特點：獲取資料並更新介面
     * - 處理使用者互動
     * - 管理介面狀態
     * - 提供即時回饋
     */
    try {
        const response = await fetch('/api/users');
        const result = await response.json();
        users = result.data;
        
        // 更新使用者介面
        displayUsers(users);
        updateUserCount(result.total);
    } catch (error) {
        showErrorMessage('載入使用者失敗');
    }
}

function displayUsers(userList) {
    // 前端：操作 DOM 元素
    const container = document.getElementById('users-container');
    container.innerHTML = userList.map(user => `
        <div class="user-card" onclick="selectUser(${user.id})">
            <h3>${user.name}</h3>
            <p>${user.email}</p>
        </div>
    `).join('');
}

// 前端特徵：
// 1. 使用者介面管理
// 2. 即時互動
// 3. 客戶端狀態管理
// 4. 響應式設計
console.log("前端：客戶端渲染，使用者互動");
```
</PythonEditor>

## 瀏覽器環境

### 理解瀏覽器作為執行時

<PythonEditor title="執行時環境差異" compare={true}>
```python !! py
# Python：指令行和伺服器環境
import os
import sys
from datetime import datetime

def server_environment_info():
    """
    Python 運行在各種環境中：
    - 指令行解釋器
    - Web 伺服器 (WSGI/ASGI)
    - 雲端容器
    - 桌面應用程式
    """
    return {
        'environment': '伺服器/桌面',
        'execution_model': '預設同步',
        'io_model': '阻塞 I/O（除非非同步）',
        'ui_framework': '無（CLI）或獨立的（tkinter, PyQt）',
        'networking': '伺服器端 HTTP 處理'
    }

# Python 環境特徵
print("Python 執行時環境：")
print(f"- 平台：{sys.platform}")
print(f"- 工作目錄：{os.getcwd()}")
print(f"- 程序 ID：{os.getpid()}")
print(f"- 時間戳：{datetime.now()}")

# 典型的 Python 工作流程
def process_data(data):
    # 伺服器處理資料
    processed = [item.upper() for item in data]
    return processed

data = ["你好", "世界", "python"]
result = process_data(data)
print(f"處理結果：{result}")
```

```javascript !! js
// JavaScript：具有獨特功能的瀏覽器環境
// 瀏覽器提供豐富的執行時環境

function browserEnvironmentInfo() {
    /**
     * JavaScript 在瀏覽器中運行，具有：
     * - HTML DOM 操作
     * - CSS 樣式控制
     * - 事件驅動程式設計
     * - 預設非同步設計
     * - 客戶端儲存
     */
    return {
        environment: '瀏覽器視窗',
        execution_model: '事件驅動和非同步',
        io_model: '預設非阻塞',
        ui_framework: '內建（HTML/CSS/DOM）',
        networking: '客戶端 HTTP 請求'
    };
}

// 瀏覽器環境特徵
console.log("JavaScript 瀏覽器環境：");
console.log(`- 使用者代理：${navigator.userAgent}`);
console.log(`- 視窗大小：${window.innerWidth}x${window.innerHeight}`);
console.log(`- URL：${window.location.href}`);
console.log(`- 時間戳：${new Date()}`);

// 瀏覽器特有功能
function demonstrateBrowserFeatures() {
    // 存取瀏覽器 API
    console.log("可用的瀏覽器 API：");
    console.log("- DOM 操作");
    console.log("- 本地儲存");
    console.log("- 地理位置");
    console.log("- 攝影機/麥克風");
    console.log("- 推播通知");
    
    // 範例：本地儲存
    localStorage.setItem('user_preference', 'dark_mode');
    console.log(`儲存的偏好：${localStorage.getItem('user_preference')}`);
}

demonstrateBrowserFeatures();
```
</PythonEditor>

## HTML-CSS-JavaScript 三位一體

### 理解三層架構

<PythonEditor title="前端技術棧" compare={true}>
```python !! py
# Python：單語言全端方式
# 模板、邏輯和樣式通常混合或分離

from flask import Flask, render_template_string

app = Flask(__name__)

# Python 方式：伺服器端渲染
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        .header { color: blue; font-size: 24px; }
        .content { margin-top: 20px; }
        .button { background: green; color: white; padding: 10px; }
    </style>
</head>
<body>
    <div class="header">{{ title }}</div>
    <div class="content">
        <p>歡迎，{{ user_name }}！</p>
        <p>您有 {{ task_count }} 個任務。</p>
        <button class="button">檢視任務</button>
    </div>
</body>
</html>
"""

@app.route('/')
def dashboard():
    """
    Python 方式：
    - 伺服器產生完整 HTML
    - 資料嵌入在模板中
    - 客戶端互動有限
    """
    return render_template_string(HTML_TEMPLATE, 
        title="任務面板",
        user_name="小明",
        task_count=5
    )

# Python 特徵：
# 1. 伺服器端渲染
# 2. 基於模板的方式
# 3. 客戶端邏輯有限
print("Python：伺服器渲染模板，嵌入資料")
```

```html !! html
<!-- HTML：結構和內容 -->
<!DOCTYPE html>
<html>
<head>
    <title>任務面板</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <!-- HTML 定義結構 -->
    <header class="header">
        <h1 id="page-title">任務面板</h1>
    </header>
    
    <main class="content">
        <div class="user-info">
            <p>歡迎，<span id="user-name">載入中...</span>！</p>
            <p>您有 <span id="task-count">0</span> 個任務。</p>
        </div>
        
        <div class="actions">
            <button id="view-tasks-btn" class="button">檢視任務</button>
            <button id="add-task-btn" class="button secondary">新增任務</button>
        </div>
        
        <div id="tasks-container" class="tasks-list">
            <!-- 任務將在這裡動態載入 -->
        </div>
    </main>
    
    <script src="script.js"></script>
</body>
</html>
```

```css !! css
/* CSS：樣式和視覺設計 */
/* CSS 處理所有視覺呈現 */

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f5f5f5;
    line-height: 1.6;
}

.header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 2rem;
    text-align: center;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.content {
    max-width: 800px;
    margin: 2rem auto;
    padding: 0 1rem;
}

.user-info {
    background: white;
    padding: 1.5rem;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    margin-bottom: 1rem;
}

.button {
    background: #28a745;
    color: white;
    border: none;
    padding: 0.75rem 1.5rem;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    margin-right: 0.5rem;
    transition: background-color 0.3s ease;
}

.button:hover {
    background: #218838;
    transform: translateY(-1px);
}

.button.secondary {
    background: #6c757d;
}

.button.secondary:hover {
    background: #5a6268;
}

.tasks-list {
    margin-top: 1rem;
}

.task-item {
    background: white;
    padding: 1rem;
    border-radius: 4px;
    margin-bottom: 0.5rem;
    border-left: 4px solid #007bff;
    transition: transform 0.2s ease;
}

.task-item:hover {
    transform: translateX(4px);
}

/* 響應式設計 */
@media (max-width: 768px) {
    .content {
        margin: 1rem;
        padding: 0;
    }
    
    .button {
        width: 100%;
        margin-bottom: 0.5rem;
    }
}
```

```javascript !! js
// JavaScript：行為和互動性
// JavaScript 處理所有動態行為

// 前端關注點分離：
// HTML = 結構，CSS = 表現，JS = 行為

class TaskDashboard {
    constructor() {
        this.tasks = [];
        this.currentUser = null;
        this.init();
    }
    
    async init() {
        /**
         * JavaScript 初始化：
         * - 設定事件監聽器
         * - 載入初始資料
         * - 配置使用者介面
         */
        await this.loadUserData();
        this.setupEventListeners();
        this.renderTasks();
    }
    
    async loadUserData() {
        // 從後端 API 獲取資料
        try {
            const userResponse = await fetch('/api/user');
            this.currentUser = await userResponse.json();
            
            const tasksResponse = await fetch('/api/tasks');
            this.tasks = await tasksResponse.json();
            
            this.updateUserInterface();
        } catch (error) {
            console.error('載入使用者資料失敗：', error);
            this.showErrorMessage('無法載入面板資料');
        }
    }
    
    updateUserInterface() {
        // 用資料更新 DOM 元素
        document.getElementById('user-name').textContent = this.currentUser.name;
        document.getElementById('task-count').textContent = this.tasks.length;
    }
    
    setupEventListeners() {
        // 事件驅動程式設計
        document.getElementById('view-tasks-btn').addEventListener('click', () => {
            this.showTasks();
        });
        
        document.getElementById('add-task-btn').addEventListener('click', () => {
            this.showAddTaskDialog();
        });
    }
    
    renderTasks() {
        const container = document.getElementById('tasks-container');
        container.innerHTML = this.tasks.map(task => `
            <div class="task-item" data-task-id="${task.id}">
                <h3>${task.title}</h3>
                <p>${task.description}</p>
                <small>截止日期：${new Date(task.dueDate).toLocaleDateString()}</small>
            </div>
        `).join('');
    }
    
    showErrorMessage(message) {
        // 建立動態錯誤通知
        const errorDiv = document.createElement('div');
        errorDiv.className = 'error-message';
        errorDiv.textContent = message;
        document.body.appendChild(errorDiv);
        
        // 3秒後自動移除
        setTimeout(() => {
            errorDiv.remove();
        }, 3000);
    }
}

// 頁面載入時初始化面板
document.addEventListener('DOMContentLoaded', () => {
    new TaskDashboard();
});

// 前端特徵：
// 1. 關注點分離（HTML/CSS/JS）
// 2. 事件驅動程式設計
// 3. 動態內容更新
// 4. 客戶端狀態管理
console.log("JavaScript：客戶端互動和動態更新");
```
</PythonEditor>

## 事件驅動程式設計模型

### 從順序執行到事件驅動

<PythonEditor title="程式設計模型對比" compare={true}>
```python !! py
# Python：順序/程序式程式設計模型
import time
from datetime import datetime

class TaskProcessor:
    def __init__(self):
        self.tasks = []
        self.completed_tasks = []
    
    def process_tasks_sequentially(self):
        """
        Python 典型方式：順序處理
        - 每一步等待前一步完成
        - 可預測的執行順序
        - 更容易調試和理解
        """
        print("開始任務處理...")
        
        # 步驟1：載入任務
        self.load_tasks()
        print("任務已載入")
        
        # 步驟2：驗證任務
        self.validate_tasks()
        print("任務已驗證")
        
        # 步驟3：處理每個任務
        for task in self.tasks:
            self.process_single_task(task)
            print(f"已處理任務：{task['title']}")
        
        # 步驟4：產生報告
        self.generate_report()
        print("報告已產生")
        
        print("所有任務已完成")
    
    def load_tasks(self):
        # 模擬從資料庫載入任務
        time.sleep(0.1)  # 模擬 I/O 延遲
        self.tasks = [
            {"id": 1, "title": "程式碼審查", "priority": "高"},
            {"id": 2, "title": "更新文件", "priority": "中"},
            {"id": 3, "title": "修復錯誤", "priority": "高"}
        ]
    
    def validate_tasks(self):
        # 驗證任務資料
        for task in self.tasks:
            if not task.get('title'):
                raise ValueError(f"任務 {task['id']} 缺少標題")
    
    def process_single_task(self, task):
        # 處理單個任務
        time.sleep(0.05)  # 模擬處理時間
        self.completed_tasks.append(task)
    
    def generate_report(self):
        # 產生完成報告
        print(f"報告：已完成 {len(self.completed_tasks)} 個任務")

# Python：順序執行
processor = TaskProcessor()
processor.process_tasks_sequentially()
```

```javascript !! js
// JavaScript：事件驅動程式設計模型
// 事件可以在任何時間、以任何順序發生

class TaskManager {
    constructor() {
        this.tasks = [];
        this.completedTasks = [];
        this.isProcessing = false;
        this.setupEventListeners();
    }
    
    setupEventListeners() {
        /**
         * JavaScript 方式：事件驅動程式設計
         * - 事件可以在任何時間發生
         * - 可以有多個待處理事件
         * - 非阻塞執行
         * - 基於回呼的處理
         */
        
        // 使用者介面事件
        document.addEventListener('click', (event) => {
            this.handleClick(event);
        });
        
        document.addEventListener('keypress', (event) => {
            this.handleKeyPress(event);
        });
        
        // 計時器事件
        setInterval(() => {
            this.autoSaveTasks();
        }, 5000); // 每5秒自動儲存
        
        // 自訂應用程式事件
        document.addEventListener('taskCompleted', (event) => {
            this.handleTaskCompletion(event.detail);
        });
        
        // 網路事件
        window.addEventListener('online', () => {
            console.log('連線已恢復 - 同步任務');
            this.syncWithServer();
        });
        
        window.addEventListener('offline', () => {
            console.log('連線遺失 - 切換到離線模式');
            this.enableOfflineMode();
        });
    }
    
    handleClick(event) {
        // 處理各種點擊事件
        const target = event.target;
        
        if (target.classList.contains('task-item')) {
            this.selectTask(target.dataset.taskId);
        } else if (target.classList.contains('complete-btn')) {
            this.completeTask(target.dataset.taskId);
        } else if (target.classList.contains('add-task-btn')) {
            this.showAddTaskDialog();
        }
    }
    
    handleKeyPress(event) {
        // 鍵盤快速鍵
        if (event.ctrlKey || event.metaKey) {
            switch (event.key) {
                case 'n':
                    event.preventDefault();
                    this.createNewTask();
                    break;
                case 's':
                    event.preventDefault();
                    this.saveTasks();
                    break;
            }
        }
    }
    
    async loadTasks() {
        // 非同步任務載入
        try {
            console.log('載入任務中...');
            const response = await fetch('/api/tasks');
            this.tasks = await response.json();
            
            // 任務載入完成時發送自訂事件
            document.dispatchEvent(new CustomEvent('tasksLoaded', {
                detail: { tasks: this.tasks }
            }));
            
            console.log(`已載入 ${this.tasks.length} 個任務`);
        } catch (error) {
            console.error('載入任務失敗：', error);
            // 優雅地處理錯誤
            this.showErrorNotification('載入任務失敗');
        }
    }
    
    completeTask(taskId) {
        // 尋找並完成任務
        const taskIndex = this.tasks.findIndex(task => task.id === taskId);
        if (taskIndex !== -1) {
            const task = this.tasks[taskIndex];
            task.completed = true;
            task.completedAt = new Date();
            
            // 移動到已完成任務
            this.completedTasks.push(task);
            this.tasks.splice(taskIndex, 1);
            
            // 發送完成事件
            document.dispatchEvent(new CustomEvent('taskCompleted', {
                detail: { task: task }
            }));
            
            // 更新介面
            this.updateTaskDisplay();
            this.showSuccessNotification(`任務"${task.title}"已完成！`);
        }
    }
    
    handleTaskCompletion(taskData) {
        // 處理任務完成事件
        console.log('任務已完成：', taskData.task.title);
        
        // 更新統計資料
        this.updateCompletionStats();
        
        // 檢查成就
        this.checkAchievements();
        
        // 自動儲存進度
        this.autoSaveTasks();
    }
    
    autoSaveTasks() {
        // 定期自動儲存
        if (this.tasks.length > 0 && !this.isProcessing) {
            console.log('自動儲存任務中...');
            this.saveTasks();
        }
    }
    
    async saveTasks() {
        // 非同步儲存
        this.isProcessing = true;
        try {
            await fetch('/api/tasks', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(this.tasks)
            });
            console.log('任務儲存成功');
        } catch (error) {
            console.error('儲存任務失敗：', error);
        } finally {
            this.isProcessing = false;
        }
    }
    
    updateTaskDisplay() {
        // 更新使用者介面
        const taskContainer = document.getElementById('tasks-container');
        taskContainer.innerHTML = this.tasks.map(task => `
            <div class="task-item" data-task-id="${task.id}">
                <h3>${task.title}</h3>
                <p>${task.description}</p>
                <button class="complete-btn" data-task-id="${task.id}">
                    完成
                </button>
            </div>
        `).join('');
    }
    
    showSuccessNotification(message) {
        // 建立並顯示通知
        const notification = document.createElement('div');
        notification.className = 'notification success';
        notification.textContent = message;
        document.body.appendChild(notification);
        
        // 自動移除通知
        setTimeout(() => {
            notification.remove();
        }, 3000);
    }
}

// DOM 準備就緒時初始化任務管理器
document.addEventListener('DOMContentLoaded', () => {
    const taskManager = new TaskManager();
    taskManager.loadTasks();
    
    console.log('任務管理器已使用事件驅動架構初始化');
});

// JavaScript 特徵：
// 1. 事件驅動程式設計
// 2. 非同步操作
// 3. 非阻塞執行
// 4. 響應式使用者介面
console.log("JavaScript：事件驅動、非同步和響應式");
```
</PythonEditor>

## 客戶端狀態管理

### 在瀏覽器中管理應用程式狀態

<PythonEditor title="狀態管理模式" compare={true}>
```python !! py
# Python：伺服器端狀態管理
# 狀態通常儲存在資料庫、會話或記憶體中

class UserSession:
    """
    Python 方式：伺服器端會話管理
    - 狀態儲存在伺服器上
    - 基於資料庫的持久化
    - 集中狀態控制
    """
    
    def __init__(self, user_id):
        self.user_id = user_id
        self.session_data = {}
        self.preferences = {}
        self.load_from_database()
    
    def load_from_database(self):
        # 模擬從資料庫載入
        self.session_data = {
            'current_page': 'dashboard',
            'selected_filters': ['active', 'high_priority'],
            'view_mode': 'grid',
            'last_activity': '2024-01-15T10:30:00Z'
        }
        
        self.preferences = {
            'theme': 'dark',
            'language': 'zh-tw',
            'notifications': True,
            'auto_save': True
        }
    
    def update_preference(self, key, value):
        """更新使用者偏好並持久化到資料庫"""
        self.preferences[key] = value
        # 儲存到資料庫
        print(f"已將偏好 {key}={value} 儲存到資料庫")
    
    def get_current_state(self):
        """獲取完整的當前狀態"""
        return {
            'session': self.session_data,
            'preferences': self.preferences,
            'user_id': self.user_id
        }
    
    def update_session(self, key, value):
        """更新會話資料"""
        self.session_data[key] = value
        print(f"會話已更新：{key}={value}")

# Python：集中式伺服器狀態
session = UserSession(user_id="user_123")
print("伺服器端狀態：", session.get_current_state())

# 更新偏好
session.update_preference('theme', 'light')
session.update_session('current_page', 'settings')

print("更新後狀態：", session.get_current_state())
```

```javascript !! js
// JavaScript：客戶端狀態管理
// 狀態在瀏覽器記憶體和本地儲存中管理

class ClientStateManager {
    /**
     * JavaScript 方式：客戶端狀態管理
     * - 狀態儲存在瀏覽器記憶體中
     * - 本地儲存用於持久化
     * - 即時狀態更新
     * - 多種狀態源
     */
    
    constructor() {
        this.state = {
            user: null,
            tasks: [],
            ui: {
                currentPage: 'dashboard',
                selectedFilters: ['active'],
                viewMode: 'grid',
                sidebarOpen: true,
                theme: 'light'
            },
            network: {
                isOnline: navigator.onLine,
                lastSync: null
            },
            cache: new Map()
        };
        
        this.listeners = new Map();
        this.initializeState();
        this.setupStateSync();
    }
    
    initializeState() {
        // 從多個來源載入狀態
        this.loadFromLocalStorage();
        this.loadFromSessionStorage();
        this.loadFromURL();
        
        console.log('客戶端狀態已初始化：', this.state);
    }
    
    loadFromLocalStorage() {
        // 持久化偏好
        const savedPreferences = localStorage.getItem('userPreferences');
        if (savedPreferences) {
            const preferences = JSON.parse(savedPreferences);
            this.state.ui = { ...this.state.ui, ...preferences };
        }
        
        // 快取資料
        const cachedTasks = localStorage.getItem('cachedTasks');
        if (cachedTasks) {
            this.state.tasks = JSON.parse(cachedTasks);
        }
    }
    
    loadFromSessionStorage() {
        // 會話特定資料
        const sessionData = sessionStorage.getItem('currentSession');
        if (sessionData) {
            const session = JSON.parse(sessionData);
            this.state.ui.currentPage = session.currentPage || 'dashboard';
            this.state.ui.selectedFilters = session.selectedFilters || ['active'];
        }
    }
    
    loadFromURL() {
        // 基於 URL 的狀態
        const urlParams = new URLSearchParams(window.location.search);
        if (urlParams.get('page')) {
            this.state.ui.currentPage = urlParams.get('page');
        }
        if (urlParams.get('filter')) {
            this.state.ui.selectedFilters = urlParams.get('filter').split(',');
        }
    }
    
    setState(path, value) {
        // 使用路徑表示法更新狀態
        const keys = path.split('.');
        let current = this.state;
        
        // 導航到目標屬性的父級
        for (let i = 0; i < keys.length - 1; i++) {
            if (!current[keys[i]]) {
                current[keys[i]] = {};
            }
            current = current[keys[i]];
        }
        
        // 設定值
        const lastKey = keys[keys.length - 1];
        const oldValue = current[lastKey];
        current[lastKey] = value;
        
        // 通知監聽器
        this.notifyListeners(path, value, oldValue);
        
        // 持久化更改
        this.persistState(path, value);
        
        console.log(`狀態已更新：${path} = ${JSON.stringify(value)}`);
    }
    
    getState(path = null) {
        if (!path) return this.state;
        
        const keys = path.split('.');
        let current = this.state;
        
        for (const key of keys) {
            if (current[key] === undefined) {
                return undefined;
            }
            current = current[key];
        }
        
        return current;
    }
    
    subscribe(path, callback) {
        // 訂閱狀態變化
        if (!this.listeners.has(path)) {
            this.listeners.set(path, []);
        }
        this.listeners.get(path).push(callback);
        
        // 返回取消訂閱函數
        return () => {
            const callbacks = this.listeners.get(path);
            const index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
        };
    }
    
    notifyListeners(path, newValue, oldValue) {
        // 通知所有相關監聽器
        for (const [listenerPath, callbacks] of this.listeners) {
            if (path.startsWith(listenerPath) || listenerPath.startsWith(path)) {
                callbacks.forEach(callback => {
                    callback(newValue, oldValue, path);
                });
            }
        }
    }
    
    persistState(path, value) {
        // 將狀態的不同部分持久化到適當的儲存中
        if (path.startsWith('ui.theme') || path.startsWith('ui.language')) {
            // 將偏好儲存到 localStorage
            const preferences = {
                theme: this.state.ui.theme,
                language: this.state.ui.language,
                viewMode: this.state.ui.viewMode
            };
            localStorage.setItem('userPreferences', JSON.stringify(preferences));
        }
        
        if (path.startsWith('ui.currentPage') || path.startsWith('ui.selectedFilters')) {
            // 將會話資料儲存到 sessionStorage
            const sessionData = {
                currentPage: this.state.ui.currentPage,
                selectedFilters: this.state.ui.selectedFilters
            };
            sessionStorage.setItem('currentSession', JSON.stringify(sessionData));
            
            // 更新 URL
            this.updateURL();
        }
        
        if (path.startsWith('tasks')) {
            // 在 localStorage 中快取任務
            localStorage.setItem('cachedTasks', JSON.stringify(this.state.tasks));
        }
    }
    
    updateURL() {
        // 更新瀏覽器 URL 以反映當前狀態
        const params = new URLSearchParams();
        params.set('page', this.state.ui.currentPage);
        params.set('filter', this.state.ui.selectedFilters.join(','));
        
        const newURL = `${window.location.pathname}?${params.toString()}`;
        window.history.replaceState(this.state, '', newURL);
    }
    
    setupStateSync() {
        // 定期與伺服器同步狀態
        setInterval(async () => {
            if (this.state.network.isOnline) {
                await this.syncWithServer();
            }
        }, 30000); // 每30秒同步一次
        
        // 處理線上/離線事件
        window.addEventListener('online', () => {
            this.setState('network.isOnline', true);
            this.syncWithServer();
        });
        
        window.addEventListener('offline', () => {
            this.setState('network.isOnline', false);
        });
    }
    
    async syncWithServer() {
        try {
            // 與伺服器同步任務
            const response = await fetch('/api/tasks');
            const serverTasks = await response.json();
            
            // 合併伺服器資料與本地更改
            this.setState('tasks', serverTasks);
            this.setState('network.lastSync', new Date().toISOString());
            
            console.log('狀態已與伺服器同步');
        } catch (error) {
            console.error('與伺服器同步失敗：', error);
        }
    }
    
    // 常見狀態操作的實用方法
    addTask(task) {
        const tasks = [...this.state.tasks, { ...task, id: Date.now() }];
        this.setState('tasks', tasks);
    }
    
    removeTask(taskId) {
        const tasks = this.state.tasks.filter(task => task.id !== taskId);
        this.setState('tasks', tasks);
    }
    
    toggleTheme() {
        const newTheme = this.state.ui.theme === 'light' ? 'dark' : 'light';
        this.setState('ui.theme', newTheme);
    }
    
    setFilter(filters) {
        this.setState('ui.selectedFilters', filters);
    }
}

// 使用範例
const stateManager = new ClientStateManager();

// 訂閱主題變化
const unsubscribeTheme = stateManager.subscribe('ui.theme', (newTheme) => {
    document.body.className = `theme-${newTheme}`;
    console.log(`主題已更改為：${newTheme}`);
});

// 訂閱任務變化
const unsubscribeTasks = stateManager.subscribe('tasks', (newTasks) => {
    console.log(`任務已更新：${newTasks.length} 個任務`);
    updateTaskDisplay(newTasks);
});

// 範例狀態變化
stateManager.setState('ui.theme', 'dark');
stateManager.addTask({ title: '學習 JavaScript', priority: 'high' });
stateManager.setFilter(['active', 'high_priority']);

function updateTaskDisplay(tasks) {
    // 使用新任務更新介面
    console.log('正在更新任務顯示，共', tasks.length, '個任務');
}

// JavaScript 狀態管理特徵：
// 1. 客戶端狀態儲存
// 2. 多種持久化機制
// 3. 即時狀態更新
// 4. 基於 URL 的狀態共用
// 5. 離線狀態管理
console.log("JavaScript：具有多層持久化的客戶端狀態管理");
```
</PythonEditor>

## 響應式設計基礎

### 適應不同螢幕尺寸

<PythonEditor title="響應式設計概念" compare={true}>
```python !! py
# Python：伺服器端裝置檢測（傳統方式）
import re
from datetime import datetime

class DeviceDetector:
    """
    傳統 Python 方式：伺服器端裝置檢測
    - 從 User-Agent 檢測裝置類型
    - 根據裝置提供不同內容
    - 即時適應能力有限
    """
    
    def __init__(self, user_agent):
        self.user_agent = user_agent
        self.device_info = self.analyze_device()
    
    def analyze_device(self):
        """從 User-Agent 分析裝置特徵"""
        ua = self.user_agent.lower()
        
        device_type = "desktop"
        if "mobile" in ua or "android" in ua or "iphone" in ua:
            device_type = "mobile"
        elif "tablet" in ua or "ipad" in ua:
            device_type = "tablet"
        
        return {
            "type": device_type,
            "is_mobile": device_type in ["mobile", "tablet"],
            "user_agent": self.user_agent
        }
    
    def get_layout_config(self):
        """返回適當的版面配置"""
        if self.device_info["type"] == "mobile":
            return {
                "columns": 1,
                "sidebar": False,
                "font_size": "16px",
                "navigation": "bottom"
            }
        elif self.device_info["type"] == "tablet":
            return {
                "columns": 2,
                "sidebar": True,
                "font_size": "18px",
                "navigation": "side"
            }
        else:  # desktop
            return {
                "columns": 3,
                "sidebar": True,
                "font_size": "14px",
                "navigation": "top"
            }
    
    def render_page(self, content):
        """根據裝置類型渲染頁面"""
        config = self.get_layout_config()
        
        page_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>任務面板</title>
            <style>
                body {{ font-size: {config['font_size']}; }}
                .container {{ columns: {config['columns']}; }}
                .sidebar {{ display: {'block' if config['sidebar'] else 'none'}; }}
                .navigation {{ position: {config['navigation']}; }}
            </style>
        </head>
        <body>
            <div class="container">
                <div class="navigation">導航</div>
                {'<div class="sidebar">側邊欄</div>' if config['sidebar'] else ''}
                <div class="content">{content}</div>
            </div>
        </body>
        </html>
        """
        
        return page_html

# 使用範例
user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)"
detector = DeviceDetector(user_agent)

print("裝置資訊：", detector.device_info)
print("版面配置：", detector.get_layout_config())

# Python 方式特徵：
# 1. 伺服器端裝置檢測
# 2. 靜態版面決策
# 3. 基於 User-Agent 的檢測
# 4. 需要頁面重新載入才能更改
print("Python：伺服器端裝置檢測和靜態版面")
```

```css !! css
/* CSS：使用媒體查詢的現代響應式設計 */
/* CSS 宣告式地處理響應式設計 */

/* 基礎樣式 - 行動裝置優先方式 */
.dashboard {
    padding: 1rem;
    max-width: 100%;
    margin: 0 auto;
}

.header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 1rem;
    margin-bottom: 1rem;
    border-radius: 8px;
}

.task-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr; /* 行動裝置單欄 */
}

.task-card {
    background: white;
    padding: 1rem;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    transition: transform 0.2s ease;
}

.task-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

.sidebar {
    display: none; /* 行動裝置隱藏 */
}

.navigation {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.nav-item {
    padding: 0.75rem;
    background: #f8f9fa;
    border-radius: 4px;
    text-decoration: none;
    color: #333;
    transition: background-color 0.2s ease;
}

.nav-item:hover {
    background: #e9ecef;
}

/* 平板樣式 - 768px 及以上 */
@media (min-width: 768px) {
    .dashboard {
        padding: 2rem;
        display: grid;
        grid-template-columns: 250px 1fr;
        gap: 2rem;
    }
    
    .sidebar {
        display: block; /* 平板上顯示側邊欄 */
    }
    
    .task-grid {
        grid-template-columns: repeat(2, 1fr); /* 平板上兩欄 */
    }
    
    .navigation {
        flex-direction: row;
        justify-content: space-around;
    }
    
    .header {
        grid-column: 1 / -1; /* 跨越全寬 */
    }
}

/* 桌面樣式 - 1024px 及以上 */
@media (min-width: 1024px) {
    .dashboard {
        max-width: 1200px;
        padding: 2rem;
    }
    
    .task-grid {
        grid-template-columns: repeat(3, 1fr); /* 桌面上三欄 */
    }
    
    .sidebar {
        position: sticky;
        top: 2rem;
        height: fit-content;
    }
    
    .navigation {
        position: sticky;
        top: 0;
        background: white;
        z-index: 100;
        padding: 1rem 0;
        border-bottom: 1px solid #eee;
    }
}

/* 大桌面樣式 - 1440px 及以上 */
@media (min-width: 1440px) {
    .task-grid {
        grid-template-columns: repeat(4, 1fr); /* 大螢幕上四欄 */
    }
    
    .dashboard {
        max-width: 1400px;
    }
}

/* 高解析度顯示器 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
    .task-card {
        border: 0.5px solid #ddd; /* 高解析度上更細的邊框 */
    }
}

/* 減少動畫偏好 */
@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* 深色模式偏好 */
@media (prefers-color-scheme: dark) {
    .dashboard {
        background-color: #1a1a1a;
        color: white;
    }
    
    .task-card {
        background: #2d2d2d;
        color: white;
    }
    
    .nav-item {
        background: #3d3d3d;
        color: white;
    }
    
    .nav-item:hover {
        background: #4d4d4d;
    }
}

/* 列印樣式 */
@media print {
    .navigation,
    .sidebar {
        display: none;
    }
    
    .task-card {
        break-inside: avoid;
        box-shadow: none;
        border: 1px solid #ddd;
    }
}
```

```javascript !! js
// JavaScript：動態響應式行為
// JavaScript 新增互動式響應式功能

class ResponsiveManager {
    /**
     * JavaScript 響應式功能：
     * - 即時螢幕尺寸檢測
     * - 動態版面調整
     * - 互動式響應式功能
     * - 效能感知適應
     */
    
    constructor() {
        this.breakpoints = {
            mobile: 768,
            tablet: 1024,
            desktop: 1440
        };
        
        this.currentBreakpoint = this.getCurrentBreakpoint();
        this.setupResponsiveHandlers();
        this.adaptInterface();
    }
    
    getCurrentBreakpoint() {
        const width = window.innerWidth;
        
        if (width < this.breakpoints.mobile) {
            return 'mobile';
        } else if (width < this.breakpoints.tablet) {
            return 'tablet';
        } else if (width < this.breakpoints.desktop) {
            return 'desktop';
        } else {
            return 'large-desktop';
        }
    }
    
    setupResponsiveHandlers() {
        // 使用防抖監聽視窗調整大小
        let resizeTimer;
        window.addEventListener('resize', () => {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(() => {
                this.handleResize();
            }, 150); // 防抖調整大小事件
        });
        
        // 監聽螢幕方向變化
        window.addEventListener('orientationchange', () => {
            setTimeout(() => {
                this.handleOrientationChange();
            }, 100); // 等待方向穩定的小延遲
        });
        
        // 監聽裝置像素比變化（縮放）
        this.setupDPRListener();
    }
    
    handleResize() {
        const newBreakpoint = this.getCurrentBreakpoint();
        
        if (newBreakpoint !== this.currentBreakpoint) {
            console.log(`中斷點已更改：${this.currentBreakpoint} → ${newBreakpoint}`);
            this.currentBreakpoint = newBreakpoint;
            this.adaptInterface();
        }
        
        // 更新檢視區資訊
        this.updateViewportInfo();
    }
    
    adaptInterface() {
        // 根據當前中斷點動態適應介面
        switch (this.currentBreakpoint) {
            case 'mobile':
                this.enableMobileMode();
                break;
            case 'tablet':
                this.enableTabletMode();
                break;
            case 'desktop':
                this.enableDesktopMode();
                break;
            case 'large-desktop':
                this.enableLargeDesktopMode();
                break;
        }
        
        this.adjustFontSizes();
        this.optimizeImages();
        this.adaptNavigation();
    }
    
    enableMobileMode() {
        // 行動裝置特定適應
        console.log('啟用行動模式');
        
        // 簡化介面
        this.setTaskColumns(1);
        this.hideSidebar();
        this.enableSwipeGestures();
        this.adjustTouchTargets();
        
        // 行動裝置效能最佳化
        this.reduceAnimations();
        this.lazyLoadImages();
    }
    
    enableTabletMode() {
        // 平板特定適應
        console.log('啟用平板模式');
        
        this.setTaskColumns(2);
        this.showSidebar();
        this.enableTouchOptimizations();
        this.adjustSpacing();
    }
    
    enableDesktopMode() {
        // 桌面特定適應
        console.log('啟用桌面模式');
        
        this.setTaskColumns(3);
        this.showSidebar();
        this.enableHoverEffects();
        this.optimizeForMouse();
    }
    
    enableLargeDesktopMode() {
        // 大桌面最佳化
        console.log('啟用大桌面模式');
        
        this.setTaskColumns(4);
        this.enableAdvancedFeatures();
        this.optimizeForHighResolution();
    }
    
    setTaskColumns(columns) {
        const taskGrid = document.querySelector('.task-grid');
        if (taskGrid) {
            taskGrid.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
        }
    }
    
    hideSidebar() {
        const sidebar = document.querySelector('.sidebar');
        if (sidebar) {
            sidebar.style.display = 'none';
        }
    }
    
    showSidebar() {
        const sidebar = document.querySelector('.sidebar');
        if (sidebar) {
            sidebar.style.display = 'block';
        }
    }
    
    enableSwipeGestures() {
        // 為行動裝置新增觸控手勢支援
        let startX, startY, currentX, currentY;
        
        document.addEventListener('touchstart', (e) => {
            startX = e.touches[0].clientX;
            startY = e.touches[0].clientY;
        });
        
        document.addEventListener('touchmove', (e) => {
            currentX = e.touches[0].clientX;
            currentY = e.touches[0].clientY;
        });
        
        document.addEventListener('touchend', () => {
            const deltaX = currentX - startX;
            const deltaY = Math.abs(currentY - startY);
            
            // 水平滑動
            if (Math.abs(deltaX) > 50 && deltaY < 100) {
                if (deltaX > 0) {
                    this.handleSwipeRight();
                } else {
                    this.handleSwipeLeft();
                }
            }
        });
    }
    
    handleSwipeRight() {
        // 導航到上一節
        console.log('向右滑動 - 向後導航');
        this.navigateToPrevious();
    }
    
    handleSwipeLeft() {
        // 導航到下一節
        console.log('向左滑動 - 向前導航');
        this.navigateToNext();
    }
    
    adjustTouchTargets() {
        // 確保行動裝置觸控目標至少為 44px
        const buttons = document.querySelectorAll('button, .clickable');
        buttons.forEach(button => {
            const rect = button.getBoundingClientRect();
            if (rect.height < 44) {
                button.style.minHeight = '44px';
                button.style.padding = '12px';
            }
        });
    }
    
    optimizeImages() {
        // 根據螢幕尺寸和像素密度最佳化影像
        const images = document.querySelectorAll('img[data-responsive]');
        const pixelRatio = window.devicePixelRatio || 1;
        
        images.forEach(img => {
            const baseWidth = img.offsetWidth;
            const optimalWidth = Math.ceil(baseWidth * pixelRatio);
            
            // 更新影像來源以獲得最佳品質
            if (img.dataset.src) {
                img.src = img.dataset.src.replace('{width}', optimalWidth);
            }
        });
    }
    
    updateViewportInfo() {
        // 更新檢視區中繼資料用於偵錯
        const viewportInfo = {
            width: window.innerWidth,
            height: window.innerHeight,
            devicePixelRatio: window.devicePixelRatio || 1,
            orientation: screen.orientation ? screen.orientation.angle : 0,
            breakpoint: this.currentBreakpoint
        };
        
        console.log('檢視區資訊：', viewportInfo);
        
        // 分發帶有檢視區資訊的自訂事件
        document.dispatchEvent(new CustomEvent('viewportChanged', {
            detail: viewportInfo
        }));
    }
    
    setupDPRListener() {
        // 監聽裝置像素比變化（縮放）
        const mediaQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
        mediaQuery.addEventListener('change', () => {
            console.log('裝置像素比已更改');
            this.optimizeImages();
        });
    }
    
    // 實用方法
    navigateToPrevious() {
        // 上一個導航的實作
        console.log('導航到上一節');
    }
    
    navigateToNext() {
        // 下一個導航的實作
        console.log('導航到下一節');
    }
    
    reduceAnimations() {
        document.body.classList.add('reduce-animations');
    }
    
    lazyLoadImages() {
        // 實作延遲載入以獲得更好的行動效能
        const images = document.querySelectorAll('img[data-lazy]');
        const imageObserver = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const img = entry.target;
                    img.src = img.dataset.lazy;
                    img.classList.remove('lazy');
                    imageObserver.unobserve(img);
                }
            });
        });
        
        images.forEach(img => imageObserver.observe(img));
    }
    
    enableHoverEffects() {
        document.body.classList.add('hover-enabled');
    }
    
    optimizeForMouse() {
        // 啟用懸停狀態和右鍵選單
        document.body.classList.add('mouse-input');
    }
    
    enableAdvancedFeatures() {
        // 啟用適合大螢幕的功能
        document.body.classList.add('advanced-features');
    }
    
    optimizeForHighResolution() {
        // 高解析度最佳化
        document.body.classList.add('high-resolution');
    }
}

// 初始化響應式管理器
const responsiveManager = new ResponsiveManager();

// 監聽檢視區變化
document.addEventListener('viewportChanged', (event) => {
    console.log('檢視區已更改：', event.detail);
    
    // 使用檢視區資訊更新分析或其他服務
    if (window.analytics) {
        window.analytics.track('viewport_changed', event.detail);
    }
});

// JavaScript 響應式特徵：
// 1. 即時響應式適應
// 2. 觸控手勢支援
// 3. 動態版面變化
// 4. 效能感知最佳化
// 5. 互動式響應式功能
console.log("JavaScript：具有即時適應的動態響應式設計");
```
</PythonEditor>

## 總結和關鍵要點

前端開發對 Python 開發者來說代表了程式設計範式的根本轉變：

### 🔄 **思維轉換**
- **伺服器端 → 客戶端**：從集中處理到分散式使用者介面
- **順序執行 → 事件驅動**：從可預測流程到響應式程式設計
- **資料處理 → 使用者體驗**：從演算法效率到使用者互動

### 🛠 **技術基礎**
- **HTML-CSS-JavaScript 三位一體**：結構、表現和行為的分離
- **瀏覽器環境**：理解獨特的功能和約束
- **狀態管理**：管理客戶端生命週期中的應用程式狀態

### 📱 **現代前端特性**
- **響應式設計**：適應各種裝置和螢幕尺寸
- **互動式介面**：建立引人入勝的使用者體驗
- **效能最佳化**：客戶端效能考量

在下一個模組中，我們將深入探討 DOM 操作和事件處理，您將學習建立動態、互動式使用者介面，即時回應使用者操作。
